package com.jl;

import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jl.config.JLCidConfig;
import lombok.SneakyThrows;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.util.*;
import java.util.concurrent.CountDownLatch;

@RestController
@RequestMapping("jcid")
public class JcidController {

    @Autowired
    private JLCidConfig jlCidConfig;

    @Autowired
    private ApplicationContext applicationContext;

    private static String line;

    private static boolean cid = false;

    @GetMapping("getServlet")
    public List<JLCidConfig.SSH> getServlet() {
        List<JLCidConfig.SSH> sshs = jlCidConfig.getSsh();
        List<JLCidConfig.SSH> result = new ArrayList<>(sshs.size());
        for (JLCidConfig.SSH ssh : sshs) {
            result.add(new JLCidConfig.SSH()
                    .setIp(ssh.getIp())
                    .setPath(ssh.getPath())
                    .setCommand(ssh.getCommand())
            );
        }
        return result;
    }

    @Async
    @GetMapping("cid")
    public void cid() {
        try {
            if (cid) {
                return;
            }
            cid = true;
            line = "";

            //打包
            String pom = System.getProperty("user.dir") + File.separator + "pom.xml";
            Runtime rt = Runtime.getRuntime();
            Process pr = rt.exec("cmd /c mvn clean & mvn install -f " + pom);
            BufferedReader input = new BufferedReader(new InputStreamReader(pr.getInputStream(), "GBK"));
            while (input.readLine() != null) {
                if (!StringUtils.isEmpty(input.readLine())) {
                    line += "<br>" + input.readLine();
                }
            }
            int exitVal = pr.waitFor();
            if (exitVal != 0) {
                line += "<br>[ERROR] " + "Exited with error code " + exitVal;
                return;
            }
            //解析pom，获得包名
            Model model = new MavenXpp3Reader().read(new FileReader(pom));
            String jar = model.getArtifactId() + "-" + model.getVersion() + ".jar";
            //上传文件到服务器
            List<JLCidConfig.SSH> sshs = jlCidConfig.getSsh();
            ThreadPoolTaskExecutor executor = applicationContext.getBean("cid-thread", ThreadPoolTaskExecutor.class);
            CountDownLatch latch = new CountDownLatch(sshs.size());
            line += "<br>[INFO] 开始上传";
            for (JLCidConfig.SSH ssh : sshs) {
                executor.execute(() -> {
                    try {
                        Session session = getSession(ssh.getIp(), ssh.getPort(), ssh.getUser(), ssh.getPassword());
                        //上传文件
                        upload(session, System.getProperty("user.dir") + File.separator + "target" + File.separator + jar, ssh.getPath() + "/" + jar);
                        //执行命令
                        if (!StringUtils.isEmpty(ssh.getCommand())) {
                            String exec = exec(session, ssh.getCommand());
                            if (!StringUtils.isEmpty(exec)) {
                                line += "<br>[INFO] " + ssh.getIp() + " exec：" + exec;
                            }
                        }
                        session.disconnect();
                        line += "<br>[INFO] " + ssh.getIp() + " success";
                    } catch (Exception e) {
                        line += "<br>[ERROR] " + e.getMessage();
                    } finally {
                        latch.countDown();
                    }
                });
            }
            latch.await();
            line += "<br>[INFO] 构建结束";
        } catch (Exception e) {
            line += "<br>[ERROR] " + e.getMessage();
        } finally {
            try {
                Thread.sleep(3000);
                cid = false;
                line = "";
            } catch (Exception es) {
                es.printStackTrace();
            }
        }
    }

    @GetMapping("getLine")
    public Map<String, Object> getLine() {
        Map<String, Object> map = new HashMap<>();
        map.put("line", line);
        map.put("cid", cid);
        return map;
    }


    private static Session getSession(String host, int port, String user, String pass) throws Exception {
        JSch jsch = new JSch();
        Session session = jsch.getSession(user, host, port);
        session.setPassword(pass);
        Properties config = new Properties();
        //在代码里需要跳过检测。否则会报错找不到主机
        config.put("StrictHostKeyChecking", "no");
        //为Session对象设置properties
        session.setConfig(config);
        //设置timeout时间
        session.setTimeout(60000);
        session.connect();
        return session;
    }

    /**
     * 上传文件
     *
     * @param session   连接对象
     * @param localPath 本地路径
     * @param sftpPath  服务器路径
     */
    private static void upload(Session session, String localPath, String sftpPath) throws Exception {
        ChannelSftp channelSftp = (ChannelSftp) session.openChannel("sftp");
        channelSftp.connect();
        channelSftp.put(localPath, sftpPath);
        channelSftp.disconnect();
    }

    /**
     * 执行命令
     *
     * @param session 连接对象
     * @param command 命令
     */
    private static String exec(Session session, String command) throws Exception {
        StringBuffer stringBuffer = new StringBuffer();
        ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
        channelExec.setCommand(command);
        channelExec.setInputStream(null);
        channelExec.setErrStream(System.err);
        channelExec.connect();
        BufferedReader in = new BufferedReader(new InputStreamReader(channelExec.getInputStream()));
        String msg;
        while ((msg = in.readLine()) != null) {
            stringBuffer.append(msg);
        }
        in.close();
        channelExec.disconnect();
        session.disconnect();
        return stringBuffer.toString();
    }
}
